import { TestBase } from "./TestBase";
import { Segment2 } from "../src/curve/Segment2";
import { Point2 } from "../src/point/Point2";
import { Vector2 } from "../src/vector/Vector2";
import { Precision } from "../src/Precision";
import { IntersectionType } from "../src/curve/IntersectionType";

export class TestSegment2 extends TestBase {
    public async runAllTests() {
        console.log('开始运行 Segment2 测试...');

        this.testConstruction();
        this.testEndPoints();
        this.testLength();
        this.testContainPoint();
        this.testProjectPoint();
        this.testIntersection();
        this.testParameterization();
        this.testTangentAndCurvature();
        this.testEdgeCases();

        console.log('Segment2 测试完成！');
    }

    /**
     * @description 测试线段构造
     */
    private testConstruction() {
        console.log('测试线段构造...');

        const p1 = new Point2(0, 0);
        const p2 = new Point2(3, 4);
        const segment = new Segment2(p1, p2);

        // 测试端点坐标
        this.assert(segment.start.equals(p1), '起点坐标错误');
        this.assert(segment.end.equals(p2), '终点坐标错误');

        // 测试克隆
        const cloned = segment.clone();
        this.assert(cloned.start.equals(p1) && cloned.end.equals(p2), '克隆错误');

        console.log('✓ 线段构造测试通过');
    }

    /**
     * @description 测试端点操作
     */
    private testEndPoints() {
        console.log('测试端点操作...');

        const segment = new Segment2(new Point2(0, 0), new Point2(3, 4));

        // 测试端点访问
        this.assert(segment.start.equals(new Point2(0, 0)), '起点访问错误');
        this.assert(segment.end.equals(new Point2(3, 4)), '终点访问错误');

        // 测试端点修改
        const newStart = new Point2(1, 1);
        const newEnd = new Point2(4, 5);
        const newSegment = new Segment2(newStart, newEnd);
        this.assert(newSegment.start.equals(newStart), '起点修改错误');
        this.assert(newSegment.end.equals(newEnd), '终点修改错误');

        console.log('✓ 端点操作测试通过');
    }

    /**
     * @description 测试长度计算
     */
    private testLength() {
        console.log('测试长度计算...');

        // 测试水平线段
        const horizontal = new Segment2(new Point2(0, 0), new Point2(3, 0));
        this.assert(Math.abs(horizontal.length - 3) < Precision.EPSILON5, '水平线段长度错误');

        // 测试垂直线段
        const vertical = new Segment2(new Point2(0, 0), new Point2(0, 4));
        this.assert(Math.abs(vertical.length - 4) < Precision.EPSILON5, '垂直线段长度错误');

        // 测试斜线段
        const diagonal = new Segment2(new Point2(0, 0), new Point2(3, 4));
        this.assert(Math.abs(diagonal.length - 5) < Precision.EPSILON5, '斜线段长度错误');

        console.log('✓ 长度计算测试通过');
    }

    /**
     * @description 测试点在线段上的判断
     */
    private testContainPoint() {
        console.log('测试点在线段上的判断...');

        const segment = new Segment2(new Point2(0, 0), new Point2(2, 2));

        // 测试端点
        this.assert(segment.containPoint(new Point2(0, 0)), '起点判断错误');
        this.assert(segment.containPoint(new Point2(2, 2)), '终点判断错误');

        // 测试线段上的点
        this.assert(segment.containPoint(new Point2(1, 1)), '中点判断错误');

        // 测试线段外的点
        this.assert(!segment.containPoint(new Point2(3, 3)), '线段外点判断错误');
        this.assert(!segment.containPoint(new Point2(1, 0)), '线外点判断错误');

        // 测试精度
        this.assert(segment.containPoint(new Point2(1, 1.000001), 0.00001), '精度判断错误');

        console.log('✓ 点在线段上的判断测试通过');
    }

    /**
     * @description 测试点到线段的投影
     */
    private testProjectPoint() {
        console.log('测试点到线段的投影...');

        const segment = new Segment2(new Point2(0, 0), new Point2(2, 0));

        // 测试垂直投影点
        const projection1 = segment.projectPoint(new Point2(1, 1));
        this.assert(projection1?.point.equals(new Point2(1, 0)), '垂直投影计算错误');
        this.assert(Math.abs(projection1?.u - 0.5) < Precision.EPSILON5, '投影参数错误');

        // 测试超出线段范围的点
        const projection2 = segment.projectPoint(new Point2(3, 1));
        this.assert(projection2?.point.equals(new Point2(2, 0)), '超出范围投影错误');
        this.assert(Math.abs(projection2?.u - 1) < Precision.EPSILON5, '超出范围参数错误');

        console.log('✓ 点到线段的投影测试通过');
    }

    /**
     * @description 测试线段相交
     */
    private testIntersection() {
        console.log('测试线段相交...');

        // 测试相交线段
        const s1 = new Segment2(new Point2(0, 0), new Point2(2, 2));
        const s2 = new Segment2(new Point2(0, 2), new Point2(2, 0));
        const intersect1 = s1.intersect(s2);
        this.assert(intersect1.type === IntersectionType.intersect, '相交判断错误');
        this.assert(intersect1.intersectPoints[0].equals(new Point2(1, 1)), '相交点计算错误');

        // 测试端点相连
        const s3 = new Segment2(new Point2(2, 2), new Point2(3, 3));
        const intersect2 = s1.intersect(s3);
        this.assert(intersect2.type === IntersectionType.connect, '端点相连判断错误');

        // 测试平行线段
        const s4 = new Segment2(new Point2(0, 1), new Point2(2, 3));
        const intersect3 = s1.intersect(s4);
        this.assert(intersect3.type === IntersectionType.disjoint, '平行线段判断错误');

        // 测试重合线段
        const s5 = new Segment2(new Point2(0, 0), new Point2(2, 2));
        const intersect4 = s1.intersect(s5);
        this.assert(intersect4.type === IntersectionType.equal, '重合线段判断错误');

        console.log('✓ 线段相交测试通过');
    }

    /**
     * @description 测试参数化
     */
    private testParameterization() {
        console.log('测试参数化...');

        const segment = new Segment2(new Point2(0, 0), new Point2(2, 2));

        // 测试参数点
        const p1 = segment.d0(0);
        const p2 = segment.d0(0.5);
        const p3 = segment.d0(1);

        this.assert(p1.equals(new Point2(0, 0)), '起点参数化错误');
        this.assert(p2.equals(new Point2(1, 1)), '中点参数化错误');
        this.assert(p3.equals(new Point2(2, 2)), '终点参数化错误');

        // 测试参数获取
        this.assert(Math.abs(segment.getParameter(new Point2(1, 1)) - 0.5) < Precision.EPSILON5, '参数获取错误');

        console.log('✓ 参数化测试通过');
    }

    /**
     * @description 测试切向量和曲率
     */
    private testTangentAndCurvature() {
        console.log('测试切向量和曲率...');

        const segment = new Segment2(new Point2(0, 0), new Point2(3, 4));
        const length = 5; // 3-4-5三角形

        // 测试切向量
        const tangent = segment.tangentAt(0.5);
        this.assert(Math.abs(tangent.x - 3 / length) < Precision.EPSILON5 &&
            Math.abs(tangent.y - 4 / length) < Precision.EPSILON5, '切向量计算错误');

        // 测试曲率（直线曲率应为0）
        this.assert(Math.abs(segment.curvatureAt(0.5)) < Precision.EPSILON5, '曲率计算错误');

        console.log('✓ 切向量和曲率测试通过');
    }

    /**
     * @description 测试边界情况
     */
    private testEdgeCases() {
        console.log('测试边界情况...');

        // 测试零长度线段
        const zeroSegment = new Segment2(new Point2(1, 1), new Point2(1, 1));
        this.assert(zeroSegment.length === 0, '零长度线段判断错误');

        // 测试精度边界
        const nearSegment = new Segment2(new Point2(0, 0), new Point2(Precision.EPSILON5, Precision.EPSILON5));
        this.assert(nearSegment.length < Precision.EPSILON3, '精度边界处理错误');

        // 测试参数范围
        try {
            const segment = new Segment2(new Point2(0, 0), new Point2(1, 1));
            segment.d0(-0.1);
            this.assert(false, '应该拒绝范围外的参数');
        } catch (e) {
            // 期望抛出异常
        }

        console.log('✓ 边界情况测试通过');
    }
}